let $Vue = null
let installed = false
class MiniRouter {
  // install 方法，是用于 vue 来注册该组件的必备方法
  static install(Vue) {
    // 判断当前插件是否被安装
    if (installed) {
      return
    }
    // 这里我使用局部变量来缓存模块安装判断
    installed = true
    // 局部保存 Vue 构造函数
    $Vue = Vue
    // console.log('route installed')
    // 当 vue 实例化之后，将 router 实例注入到 vue 实例中
    $Vue.mixin({
      beforeCreate() {
        if (this.$options.router) {
          $Vue.prototype.$router = this.$options.router
          // console.log('hash: ' + window.location.hash)
          // this.$options.router.data.current = window.location.hash.replace(/#/, '')
        }
      }
    })
  }

  constructor(options) {
    this.mode = 'hash'
    this.options = options
    this.routes = options.routes
    // 课程中使用的使 routeMap 来匹配组件，但这里我会改进这种方式，使用 match 方法来匹配路由

    // 创建一个响应式的属性 current，当 current 改变的时候就会重新渲染 dom
    this.data = $Vue?.observable({
      current: window.location.hash.replace(/#/, '')
    })

    this.init()
  }

  init() {
    this.initComponents()
    this.initEvent()
  }

  // 初始化内置组件
  initComponents() {
    // 初始化一个 route-link 组件
    $Vue.component('router-link', {
      props: {
        to: String
      },
      render(h) {
        return h('a', {
          attrs: {
            href: this.to
          },
          on: {
            click: this.clickHandler
          }
        }, [this.$slots.default])
      },
      methods: {
        clickHandler(e) {
          history.pushState({}, '', `#${this.to}`)
          this.$router.data.current = this.to
          e.preventDefault()
        }
      }
    })

    const self = this
    // 初始化一个 router-view 组件，进行组件渲染
    $Vue.component('router-view', {
      render(h) {
        let cm = self.match(self.data.current)?.component
        return h(cm)
      }
    })
  }

  // match 方法可以根据 path 匹配到对应的路由对象，并返回
  match(path) {
    // 存储匹配到的路由及部分参数
    let matchRoute = null
    // 遍历所有的路由，知道匹配到对应的路由
    this.routes.every(route => {
      // 这里的路由分为两类，动态路由和静态路由
      let routeType = this.getRouteType(route.path)
      let routeMethod = this[`match${routeType}Route`]
      if (!routeMethod) {
        return true
      }
      // 判断路由是否匹配，并返回对应的数据参数
      let matchResult = routeMethod(path, route)
      if (matchResult) {
        // 匹配成功，跳出循环
        matchRoute = matchResult
        return false
      }
      return true
    })
    return matchRoute
  }

  // 区分路由规则的类型
  getRouteType(path) {
    let type = 'static'
    // 动态路由判断
    let dynamic_reg = /[\d\D]\/:[\d\D]/
    if (dynamic_reg.test(path)) {
      // 匹配动态路由
      type = 'dynamic'
    } else if (path === '*') {
      type = 'wildcard'
    }

    return type.replace(/^[A-z]/, ($) => $.toUpperCase())
  }

  // 匹配静态路由，匹配成功返回 false
  matchStaticRoute(path, route) {
    if (path === route.path) {
      return {
        component: route.component
      }
    }
    return false
  }

  matchDynamicRoute(path, route) {
    let rule = route.path.split(':')
    // 判断是否相同的路由前缀
    if (!path.includes(rule[0])) {
      return false
    }
    // 去掉相同的前缀，剩余后缀作为变量
    let param = path.replace(rule[0], '')
    return {
      params: {
        [rule[1]]: param
      },
      component: route.component
    }
  }

  matchWildcardRoute(path, route) {
    if (route.path === '*') {
      return {
        component: route.component
      }
    }
    return false
  }

  initEvent() {
    document.addEventListener('popstate', () => {
      this.data.current = window.location.hash.replace(/#/, '')
    })
  }
}

module.exports = MiniRouter
